package com.audaque.springboot.foshanupload.web.reactivedemo.reactor;

import com.audaque.springboot.foshanupload.web.reactivedemo.handler.MyAcceptorHandler;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author zgb
 * @desc 主从Reactor多线程
 * @date 2023-08-08 21:35:47
 */
@Component
@Getter
@Setter
@Slf4j
public class MultiThreadEchoServerReactor {
    private ServerSocketChannel serverSocket;
    private AtomicInteger next = new AtomicInteger(0);
    private Selector bossSelector;
    private Reactor bossReactor;
    //selectors集合,引入多个selector选择器
    //多个选择器可以更好的提高通道的并发量
    private Selector[] workSelectors = new Selector[2];
    //引入多个子反应器
    //如果CPU是多核的可以开启多个子Reactor反应器,这样每一个子Reactor反应器还可以独立分配一个线程。
    //每一个线程可以单独绑定一个单独的Selector选择器以提高通道并发量
    private Reactor[] workReactors;


    @Autowired
    private MyAcceptorHandler myAcceptorHandler;

    @PostConstruct
    public void init() throws IOException {
        bossSelector = Selector.open();
        //初始化多个selector选择器
        workSelectors[0] = Selector.open();
        workSelectors[1] = Selector.open();
        serverSocket = ServerSocketChannel.open();

        InetSocketAddress address = new InetSocketAddress("localhost", 9501);
        serverSocket.socket().bind(address);
        //非阻塞
        serverSocket.configureBlocking(false);

        //第一个selector,负责监控新连接事件
        SelectionKey sk = serverSocket.register(bossSelector, SelectionKey.OP_ACCEPT);
        //附加新连接处理handler处理器到SelectionKey（选择键）
        sk.attach(myAcceptorHandler);

        //处理新连接的反应器
        bossReactor = new Reactor(bossSelector);

        //第一个子反应器，一子反应器负责一个选择器
        Reactor subReactor1 = new Reactor(workSelectors[0]);
        //第二个子反应器，一子反应器负责一个选择器
        Reactor subReactor2 = new Reactor(workSelectors[1]);
        workReactors = new Reactor[]{subReactor1, subReactor2};
    }


    public void startService() {
        new Thread(bossReactor).start();
        // 一子反应器对应一条线程
        new Thread(workReactors[0]).start();
        new Thread(workReactors[1]).start();
    }


}
